package com.example.Util;

import android.annotation.SuppressLint;
import android.content.Context;

import java.util.Timer;
import java.util.TimerTask;

public class CommandProc {

    private static short cmdCode;//功能码
    private static boolean threadWork;
    private static boolean cmdDone = true;
    private static boolean sendResult;
    private Boolean cancelRet;
    byte[] binImage;
    // listener
    private IFPListener.FPCommandListener fpCommandListener;
    private IFPListener.FPCancelListener fpCancelListener;
    private IFPListener.FPCancelWaitListener cancelWaitInterface;
    private TimerTask task;
    private DevComm devComm;
    int imgWidth, imgHeight;
    private final byte[] templateData = new byte[DevComm.SZ_MAX_RECORD_SIZE];
    private int m_nTemplateSize = 0;

    public CommandProc() {
        threadWork = false;
        binImage = new byte[1024 * 100];
    }

    public Boolean openDevice(Context context) {
        if (devComm == null) {
            devComm = new DevComm();
        }
        boolean isOpen = devComm.openComm(context);
        if (isOpen) {
            LogAndToastUtils.log("打开设备成功");
            new Thread(() -> getFingerTemplate(false)).start();
            return true;
        } else {
            LogAndToastUtils.log("打开设备失败");
            return false;
        }
    }

    public CloseResult closeDevice() {
        if (task != null) {
            task.cancel();
        }
        if (threadWork) {
            cancel();
            return new CloseResult(false, "有功能未关闭，再次点击关闭按钮...");
        }
        boolean close = devComm.closeComm();// success
        if (close) {
            LogAndToastUtils.log("关闭成功");
            return new CloseResult(true, "关闭成功");
        } else {
            LogAndToastUtils.log("关闭失败");
            return new CloseResult(false, "关闭失败");
        }

    }


    public void setListener(IFPListener.FPCommandListener fpCommandListener, IFPListener.FPCancelListener fpCancelListener) {
        this.fpCommandListener = fpCommandListener;
        this.fpCancelListener = fpCancelListener;
    }


    public void cancel() {

        new Thread(() -> {
            boolean w_bRet;
            devComm.initPacket2((short) DevComm.CMD_FP_CANCEL_CODE, true);
            devComm.setDataLen2((short) 0x00);
            devComm.addCheckSum2(true);
            w_bRet = devComm.USB_SendPacket2((short) DevComm.CMD_FP_CANCEL_CODE);
            if (!w_bRet) {
                cancelRet = false;
                fpCancelListener.cancelRunning(runCancelRet);
                return;
            }
            while (!cmdDone) {
                try {
                    Thread.currentThread().sleep(1);//毫秒
                } catch (Exception ignored) {
                }
            }

            w_bRet = devComm.USB_ReceiveAck2();
            if (w_bRet) {
                cancelRet = true;
                fpCancelListener.cancelRunning(runCancelRet);
            } else {
                cancelRet = false;
                fpCancelListener.cancelRunning(runCancelRet);
            }

        }).start();

    }

    public void enRollTemplate(final int p_nTmpNo, final boolean p_bForce) {
        boolean checkRet = checkForce(p_bForce, p_nRetCode -> enRollTemplate(p_nTmpNo, p_bForce));
        if (!checkRet) {
            return;
        }
        runCommand1P((short) DevComm.CMD_ENROLL_CODE, (short) p_nTmpNo);
    }

    public void verify(final int p_nTmpNo, final boolean p_bForce) {
        boolean checkRet = checkForce(p_bForce, p_nRetCode -> verify(p_nTmpNo, p_bForce));
        if (!checkRet) {
            return;
        }
        runCommand1P((short) DevComm.CMD_VERIFY_CODE, (short) p_nTmpNo);

    }

    Runnable runCancelRet = new Runnable() {
        @Override
        public void run() {
            fpCancelListener.cancelReturn(0, cancelRet, cmdCode);
            if (cancelWaitInterface != null) {
                cancelWaitInterface.runProcReturn(0);
            }
            if (task != null) {
                task.cancel();
            }

        }
    };

    public void identify(final boolean p_bForce) {
        if (task != null) {
            task.cancel();
        }
        boolean checkRet = checkForce(p_bForce, p_nRetCode -> identify(p_bForce));
        if (!checkRet) {
            return;
        }

        runCommandNP((short) DevComm.CMD_IDENTIFY_CODE);
    }

    public void fingerState(final boolean p_bForce) {
        boolean checkRet = checkForce(p_bForce, p_nRetCode -> fingerState(p_bForce));
        if (!checkRet) {
            return;
        }

        runCommandNP((short) DevComm.CMD_FINGER_DETECT_CODE);

    }

    public void changeTemplateID(int p_nTmpNo, boolean p_bForce) {
        boolean checkRet = checkForce(p_bForce, p_nRetCode -> verify(p_nTmpNo, p_bForce));
        if (!checkRet) {
            return;
        }
        runCommand1P((short) DevComm.CMD_CHANGE_TEMPLATE_CODE, (short) p_nTmpNo);

    }

    public void deleteID(final int p_nTmpNo, final boolean p_bForce) {


        boolean checkRet = checkForce(p_bForce, p_nRetCode -> deleteID(p_nTmpNo, p_bForce));
        if (!checkRet) {
            return;
        }

        runCommand1P((short) DevComm.CMD_CLEAR_TEMPLATE_CODE, (short) p_nTmpNo);

    }

    public void deleteAll(final boolean p_bForce) {

        boolean checkRet = checkForce(p_bForce, p_nRetCode -> deleteAll(p_bForce));
        if (!checkRet) {
            return;
        }
        runCommandNP((short) DevComm.CMD_CLEAR_ALLTEMPLATE_CODE);
    }

    private void runCommandNP(short p_wCmd) {
        // assemble command packet
        devComm.initPacket(p_wCmd, true);
        devComm.addCheckSum(true);

        cmdCode = p_wCmd;
        startSendThread();
    }

    /**
     * @param p_wCmd  输入的功能码
     * @param p_wData 模板编号等
     */
    private void runCommand1P(short p_wCmd, short p_wData) {
        LogAndToastUtils.log("Run_Command_1P");
        devComm.initPacket(p_wCmd, true);
        devComm.setDataLen((short) 0x0002);
        devComm.setCmdData(p_wData, true);
        devComm.addCheckSum(true);
        cmdCode = p_wCmd;
        startSendThread();
    }


    @SuppressLint("CheckResult")
    public void startSendThread() {
        cmdDone = false;
        while (threadWork) {
            try {
                Thread.currentThread().sleep(1);//毫秒
            } catch (Exception ignored) {
            }
        }
        new Thread(() -> {
            boolean w_blRet;
            short w_wPrefix;
            threadWork = true;
            //w_wPrefix()获取包识别码
            w_wPrefix = (short) (((devComm.abyPacket[1] << 8) & 0x0000FF00) | (devComm.abyPacket[0] & 0x000000FF));
            if (w_wPrefix == (short) (DevComm.CMD_PREFIX_CODE)) {//开始
                if (cmdCode != (short) (DevComm.CMD_FP_CANCEL_CODE)) {
                    w_blRet = devComm.USB_SendPacket(cmdCode);//发送数据包
                } else {
                    w_blRet = devComm.USB_ReceiveAck(cmdCode);
                }
            } else if (w_wPrefix == (short) (DevComm.CMD_DATA_PREFIX_CODE)) {
                w_blRet = devComm.USB_SendDataPacket(cmdCode);
            } else {
                if (cmdCode != (short) (DevComm.CMD_FEATURE_OF_CAPTURED_FP_CODE)) {
                    LogAndToastUtils.log("m_dwCode != (short) (DevComm.CMD_FEATURE_OF_CAPTURED_FP_CODE)");
                    w_blRet = devComm.USB_ReceiveAck(cmdCode);
                } else {
                    w_blRet = devComm.USB_ReceiveDataPacket((short) DevComm.CMD_FEATURE_OF_CAPTURED_FP_CODE);
                }
            }
            sendResult = w_blRet;
            fpCommandListener.cmdProcRunning(procRspPacket);
            threadWork = false;
        }).start();


    }

    Runnable procRspPacket = new Runnable() {
        public void run() {
            if (!sendResult) {
                cmdDone = true;
                if (cmdCode == (short) DevComm.CMD_FINGER_DETECT_CODE) {
                    LogAndToastUtils.log("Identify Open: ");
                    identify(false);
                }
                return;
            }
            sendResponseRet();
        }
    };

    //处理响应包
    private void sendResponseRet() {
        short w_nCmd;
        short w_nRet;
        short w_nData1, w_nData2;
        //获取功能码
        w_nCmd = (short) (((devComm.abyPacket[3] << 8) & 0x0000FF00) | (devComm.abyPacket[2] & 0x000000FF));
        w_nRet = devComm.MAKEWORD(devComm.abyPacket[6], devComm.abyPacket[7]);
        w_nData1 = devComm.MAKEWORD(devComm.abyPacket[8], devComm.abyPacket[9]);
        w_nData2 = devComm.MAKEWORD(devComm.abyPacket[10], devComm.abyPacket[11]);
        LogAndToastUtils.log("sendResponseRet()");
        fpCommandListener.cmdProcReturn(w_nCmd, w_nRet, w_nData1, w_nData2);
        loopResponse(w_nCmd, w_nRet, w_nData1);
    }

    private void loopResponse(int p_nCode, int p_nRet, int p_nParam1) {
        if ((p_nCode == (short) DevComm.CMD_IDENTIFY_FREE_CODE)) {
            if (p_nRet == (short) DevComm.ERR_SUCCESS ||
                    devComm.LOBYTE((short) p_nParam1) != DevComm.ERR_NOT_AUTHORIZED &&
                            devComm.LOBYTE((short) p_nParam1) != DevComm.ERR_FP_CANCEL &&
                            devComm.LOBYTE((short) p_nParam1) != DevComm.ERR_INVALID_OPERATION_MODE &&
                            devComm.LOBYTE((short) p_nParam1) != DevComm.ERR_ALL_TMPL_EMPTY) {
                devComm.memset(devComm.abyPacket, (byte) 0, 64 * 1024);
                startSendThread();
                return;
            }
        }
        if ((p_nCode == (short) DevComm.CMD_ENROLL_CODE) ||
                (p_nCode == (short) DevComm.CMD_CHANGE_TEMPLATE_CODE)) {
            switch (p_nParam1) {
                case (short) DevComm.NEED_RELEASE_FINGER:
                case (short) DevComm.NEED_FIRST_SWEEP:
                case (short) DevComm.NEED_SECOND_SWEEP:
                case (short) DevComm.NEED_THIRD_SWEEP:
                case (short) DevComm.ERR_BAD_QUALITY:
                    devComm.memset(devComm.abyPacket, (byte) 0, 64 * 1024);
                    startSendThread();
                    return;
                default:
                    break;
            }
        }
        if ((p_nCode == (short) DevComm.CMD_ENROLL_ONETIME_CODE) || (p_nCode == (short) DevComm.CMD_VERIFY_CODE) ||
                (p_nCode == (short) DevComm.CMD_IDENTIFY_CODE) || (p_nCode == (short) DevComm.CMD_IDENTIFY_FREE_CODE)) {
            if (p_nParam1 == (short) DevComm.NEED_RELEASE_FINGER) {
                devComm.memset(devComm.abyPacket, (byte) 0, 64 * 1024);
                startSendThread();
                return;
            }
        }
        devComm.memset(devComm.abyPacket, (byte) 0, 64 * 1024);
        cmdDone = true;
        if (p_nCode == (short) DevComm.CMD_FEATURE_OF_CAPTURED_FP_CODE) {
            LogAndToastUtils.log("ProcResponsePacket: CMD_UP_IMAGE_CODE");
            //writeTemplate(10, templateData, DevComm.SZ_TEMPLATE_SIZE, false);
            /*task = new TimerTask() {
                @Override
                public void run() {
                    getFingerImageData(false);
                }
            };
            Timer timer = new Timer();
            timer.schedule(task, 1000); //延迟1秒再次开启指纹识别*/

        }
    }

    /**
     * @param retry   是否重新开启功能
     * @param iCancel 取消功能结果返回
     * @return true = 有功能正在使用设备
     */
    private boolean checkForce(boolean retry, IFPListener.FPCancelWaitListener iCancel) {
        cancelWaitInterface = null;
        // check force
        if (!cmdDone) {
            if (!retry) {
                LogAndToastUtils.log("CheckForce: " + false);
                return false;
            }

            cancelWaitInterface = iCancel;
            cancel();
            return false;
        }
        return true;
    }


    public void getFingerImageData(boolean force) {

        boolean checkForce = checkForce(force, new IFPListener.FPCancelWaitListener() {
            @Override
            public void runProcReturn(int p_nRetCode) {
                getFingerImageData(force);
            }
        });

        if (!checkForce) {
            return;
        }
        cmdDone = false;

        while (threadWork) {
            try {
                Thread.currentThread().sleep(1);//毫秒
            } catch (Exception ignored) {
                LogAndToastUtils.log(ignored.getMessage());
            }
        }

        new Thread(() -> {
            boolean w_blRet;
            threadWork = true;
            devComm.initPacket((short) DevComm.CMD_UP_IMAGE_CODE, true);
            devComm.setDataLen((short) 0x00);
            devComm.addCheckSum(true);

            cmdCode = (short) DevComm.CMD_UP_IMAGE_CODE;

            w_blRet = devComm.sendCommand((short) DevComm.CMD_UP_IMAGE_CODE);
            if (!w_blRet) {
                sendResult = false;
                fpCommandListener.cmdProcRunning(procRspPacket);
                threadWork = false;
                return; // goto
            }

            if (devComm.getRetCode() != DevComm.ERR_SUCCESS) {
                sendResult = true;
                fpCommandListener.cmdProcRunning(procRspPacket);
                threadWork = false;
                return; // goto
            }

            imgWidth = (short) (((devComm.abyPacket[9] << 8) & 0x0000FF00) | (devComm.abyPacket[8] & 0x000000FF));
            imgHeight = (short) (((devComm.abyPacket[11] << 8) & 0x0000FF00) | (devComm.abyPacket[10] & 0x000000FF));

            w_blRet = devComm.USB_ReceiveImage(binImage, imgWidth * imgHeight);

            sendResult = w_blRet;
            fpCommandListener.cmdProcRunning(runSetImage);
            fpCommandListener.cmdProcRunning(procRspPacket);
            threadWork = false;
        }).start();


    }

    public void writeTemplate(int no, byte[] w_pTemplate, int w_nTemplateSize, boolean force) {
        boolean w_blRet;
        int w_nTemplateNo;
        int i, n, r;
        int w_nIntRet = 0;
        // check force
        checkForce(force, new IFPListener.FPCancelWaitListener() {
            @Override
            public void runProcReturn(int p_nRetCode) {
                writeTemplate(no, w_pTemplate, w_nTemplateSize, force);
            }
        });

        imgWidth = DevComm.SZ_IMG_WIDTH;
        imgHeight = DevComm.SZ_IMG_HEIGHT;
        System.arraycopy(w_pTemplate, 0, templateData, 0, w_nTemplateSize);

        w_nTemplateNo = no;

        // assemble command packet
        devComm.initPacket((short) DevComm.CMD_WRITE_TEMPLATE_CODE, true);
        devComm.setDataLen((short) 0x0002);
        devComm.setCmdData((short) m_nTemplateSize, true);
        devComm.addCheckSum(true);

        // send command packet to target
        w_blRet = devComm.sendCommand((short) DevComm.CMD_WRITE_TEMPLATE_CODE);
        if (!w_blRet) {
            closeDevice();
            return;
        }

        if (devComm.getRetCode() != (short) DevComm.ERR_SUCCESS) {
            sendResponseRet();
            return;
        }

        if ((m_nTemplateSize == DevComm.SZ_TEMPLATE_SIZE) || (m_nTemplateSize == DevComm.OLD_USER_TEMPLATE_SIZE)) {
            // assemble data packet
            devComm.initPacket((short) DevComm.CMD_WRITE_TEMPLATE_CODE, false);
            devComm.setDataLen((short) (m_nTemplateSize + 2));
            devComm.setCmdData((short) w_nTemplateNo, true);
            System.arraycopy(templateData, 0, devComm.abyPacket, 8, m_nTemplateSize);
            devComm.addCheckSum(false);

            // send data packet to target
            w_blRet = devComm.USB_SendDataPacket((short) DevComm.CMD_WRITE_TEMPLATE_CODE);
            LogAndToastUtils.log("(m_nTemplateSize == DevComm.SZ_TEMPLATE_SIZE)w_blRet:" + w_blRet);
            if (!w_blRet) {
                return;
            }
        } else {
            n = m_nTemplateSize / DevComm.DATA_SPLIT_UNIT;
            r = m_nTemplateSize % DevComm.DATA_SPLIT_UNIT;

            for (i = 0; i < n; i++) {
                // assemble data packet
                devComm.initPacket((short) DevComm.CMD_WRITE_TEMPLATE_CODE, false);
                devComm.setDataLen((short) (DevComm.DATA_SPLIT_UNIT + 4));
                devComm.setCmdData((short) w_nTemplateNo, true);
                devComm.setCmdData((short) DevComm.DATA_SPLIT_UNIT, false);
                System.arraycopy(templateData, i * DevComm.DATA_SPLIT_UNIT, devComm.abyPacket, 10, DevComm.DATA_SPLIT_UNIT);
                devComm.addCheckSum(false);

                // send data packet to target
                w_blRet = devComm.USB_SendDataPacket((short) DevComm.CMD_WRITE_TEMPLATE_CODE);
                if (!w_blRet) {
                    return;
                }
                LogAndToastUtils.log("else1 w_blRet：" + w_blRet);
            }

            if (r > 0) {
                devComm.initPacket((short) DevComm.CMD_WRITE_TEMPLATE_CODE, false);
                devComm.setDataLen((short) (r + 4));
                devComm.setCmdData((short) w_nTemplateNo, true);
                devComm.setCmdData((short) (r & 0xFFFF), false);
                System.arraycopy(templateData, i * DevComm.DATA_SPLIT_UNIT, devComm.abyPacket, 10, r);
                devComm.addCheckSum(false);

                //. Send data packet to target
                w_blRet = devComm.USB_SendDataPacket((short) DevComm.CMD_WRITE_TEMPLATE_CODE);
                if (!w_blRet) {
                    return;
                }
                LogAndToastUtils.log("else 2w_blRet：" + w_blRet);
            }
        }

        // display response packet
        sendResponseRet();

    }

    public void getFingerTemplate(boolean force) {
        int w_nIntRet;

        // check force
        boolean checkForce = checkForce(force, new IFPListener.FPCancelWaitListener() {
            @Override
            public void runProcReturn(int p_nRetCode) {
                getFingerTemplate(force);
            }
        });
        if (!checkForce) {
            return;
        }
        cmdDone = false;

        while (threadWork) {
            try {
                Thread.sleep(1);//毫秒
            } catch (Exception e) {
            }
        }

        new Thread(new Runnable() {
            public void run() {
                boolean w_blRet = false;
                int w_nLen = 0;
                int w_nBufOffset = 0;

                threadWork = true;

                devComm.memset(templateData, (byte) 0, DevComm.SZ_TEMPLATE_SIZE);

                //. Assemble command packet
                devComm.initPacket((short) DevComm.CMD_FEATURE_OF_CAPTURED_FP_CODE, true);
                devComm.addCheckSum(true);

                cmdCode = DevComm.CMD_FEATURE_OF_CAPTURED_FP_CODE;
                w_blRet = devComm.sendCommand((short) DevComm.CMD_FEATURE_OF_CAPTURED_FP_CODE);
                if (!w_blRet) {
                    sendResult = false;
                    fpCommandListener.cmdProcRunning(procRspPacket);
                    threadWork = false;
                    return; // goto
                }

                if (devComm.getRetCode() != DevComm.ERR_SUCCESS) {
                    sendResult = true;
                    fpCommandListener.cmdProcRunning(procRspPacket);
                    threadWork = false;
                    return; // goto
                }

                // Receive template data
                if (devComm.getCmdData(false) == DevComm.SZ_TEMPLATE_SIZE) {
                    w_blRet = devComm.USB_ReceiveDataPacket((short) DevComm.CMD_FEATURE_OF_CAPTURED_FP_CODE);
                    w_nLen = DevComm.SZ_TEMPLATE_SIZE;
                    System.arraycopy(devComm.abyPacket, 8, templateData, 0, DevComm.SZ_TEMPLATE_SIZE);
                } else {
                    w_nLen = devComm.getCmdData(false);
                    while (true) {
                        w_blRet = devComm.USB_ReceiveDataPacket((short) DevComm.CMD_FEATURE_OF_CAPTURED_FP_CODE);
                        if (!w_blRet) {
                            break;
                        } else {
                            if (devComm.getRetCode() == DevComm.ERR_SUCCESS) {
                                if (devComm.getDataLen() > (DevComm.DATA_SPLIT_UNIT + 2)) {
                                    devComm.setCmdData((short) DevComm.ERR_FAIL, true);
                                    devComm.setCmdData((short) DevComm.ERR_INVALID_PARAM, false);
                                    w_blRet = false;
                                    break;
                                } else {
                                    System.arraycopy(devComm.abyPacket, 8, templateData, w_nBufOffset, devComm.getDataLen() - 2);
                                    w_nBufOffset = w_nBufOffset + (devComm.getDataLen() - 2);
                                    if (w_nBufOffset == w_nLen) {
                                        break;
                                    }
                                }
                            } else {
                                w_blRet = false;
                                break;
                            }
                        }
                    }
                }

                if (!w_blRet) {
                    sendResult = false;
                    fpCommandListener.cmdProcRunning(procRspPacket);
                    threadWork = false;
                    return; // goto
                } else {
                    sendResult = true;
                    fpCommandListener.cmdProcRunning(runSetTemplate);
                    fpCommandListener.cmdProcRunning(procRspPacket);
                    m_nTemplateSize = w_nLen;
                    threadWork = false;
                }
            }
        }).start();


    }

    Runnable runSetTemplate = new Runnable() {
        @Override
        public void run() {
            fpCommandListener.cmdImageProcReturn(templateData, m_nTemplateSize);
        }
    };
    Runnable runSetImage = new Runnable() {
        @Override
        public void run() {
            fpCommandListener.cmdImageProcReturn(binImage, imgWidth * imgHeight);
        }
    };


}
